OPC Studio User's Guide and Reference
Push Data Provision Model
Concepts > OPC Wizard Concepts > OPC Wizard Operation Model > Data Provision And Consumption Models > Push Data Provision Model
In This Topic

General

In the push data provision model, you write an independently running code that gathers the data from the underlying system and stores (pushes) it into the data variables. The OPC Wizard then simply uses this data whenever an OPC Read (or optionally subscription update) is made.

OPC Wizard retrieves the data (for OPC Reads and data subscriptions) from the ReadAttributeData Property of the data variable. The task of your code is to store the data into this property, using the UpdateReadAttributeData Method. When and how it is done is up to you, and depends highly on the specifics of the OPC server you are developing. In many cases, it can be based on some kind of periodic timer. In other scenarios, it may be triggered by the underlying system or connected device.

When the push data provision model is used, it cannot be guaranteed that the data the OPC client receives is "up to date", i.e. collected at the time when the OPC Read (or subscription update) was made. This is because there is effectively a "cache" in the form of the ReadAttributeData Property in the data variable between the underlying system and the OPC server, and the requests are fulfilled from the cache and not by actually reading from the underlying system at that moment. The push data provision model can therefore be only used in applications where this behavior does not pose a problem.

Conversely, the advantage of the push data provision model is that OPC Reads are fulfilled very quickly, as the code that retrieves the data from the underlying system cannot "block" the OPC server.

The following picture illustrates how push data provision model works.

 

For performance reasons, consider setting the PropagateRead Property of the data variable to false in this model. Since there is no handling of the Read Event or overriding of the OnRead Method in the push data provision model, Read request propagation is unnecessary.

.NET

// This example shows how to update the read value in the push data provision model. In this model, your code pushes the
// data into the server, and the server then makes the data available to OPC clients.
// You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-OPCStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using System.Timers;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.NodeSpace;

namespace UAServerDocExamples._UADataVariable
{
    class UpdateReadAttributeData
    {
        public static void Main1()
        {
            // Instantiate the server object.
            // By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            var server = new EasyUAServer();

            // Create a read-only data variable.
            var dataVariable = UADataVariable.CreateIn(server.Objects, "ReadThisVariable")
                .ValueType<int>()
                .Writable(false);

            // Create a timer for pushing the data for OPC reads. In a real server the activity may also come from other
            // sources.
            var timer = new Timer
            {
                Interval = 1000,
                AutoReset = true,
            };

            // Set the read attribute data of the data variable to a random value whenever the timer interval elapses.
            var random = new Random();
            timer.Elapsed += (sender, args) => dataVariable.UpdateReadAttributeData(random.Next());
            timer.Start();

            // Start the server.
            Console.WriteLine("The server is starting...");
            server.Start();

            Console.WriteLine("The server is started.");
            Console.WriteLine();

            // Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...");
            Console.ReadLine();

            // Stop the server.
            Console.WriteLine("The server is stopping...");
            server.Stop();

            // Stop the timer.
            timer.Stop();

            Console.WriteLine("The server is stopped.");
        }
    }
}
' This example shows how to update the read value in the push data provision model. In this model, your code pushes the
' data into the server, and the server then makes the data available to OPC clients.
' You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports System
Imports System.Timers
Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.NodeSpace

Namespace _UADataVariable
    Partial Friend Class UpdateReadAttributeData
        Shared Sub Main1()
            ' Instantiate the server object.
            ' By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            Dim server = New EasyUAServer()

            ' Create a read-only data variable.
            Dim dataVariable = UADataVariable.CreateIn(server.Objects, "ReadThisVariable") _
                .ValueType(Of Integer)() _
                .Writable(False)

            ' Create a timer for pushing the data for OPC reads. In a real server the activity may also come from other
            ' sources.
            Dim timer = New Timer With
            {
                .Interval = 1000,
                .AutoReset = True
            }

            ' Set the read attribute data of the data variable to a random value whenever the timer interval elapses.
            Dim random = New Random()
            AddHandler timer.Elapsed, Sub(sender, args) dataVariable.UpdateReadAttributeData(random.Next())
            timer.Start()

            ' Start the server.
            Console.WriteLine("The server is starting...")
            server.Start()

            Console.WriteLine("The server is started.")
            Console.WriteLine()

            ' Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...")
            Console.ReadLine()

            ' Stop the server.
            Console.WriteLine("The server is stopping...")
            server.Stop()

            Console.WriteLine("The server is stopped.")
        End Sub
    End Class
End Namespace

Initial Data

When using the push data provision model, in general, you are responsible for providing the initial contents of the ReadAttributeData Property. Make sure you have a reasonable contents even before your code first gets a chance to push the "real" data to the data variable. WIthout further configuration, the ReadAttributeData Property contains empty data - meaning that the status code is "Good", but there is no timestamp, and the value itself is null. This may not be what you want - especially with non-nullable data types where null is not even a valid value for the data type of your variable; a conversion error occurs in such case, see OPC Wizard Error Model. You always need to specify initial data that make sense for your server or application.

Many extension methods for Data Variable Configuration already initialize the ReadAttributeData Property at least to the default value for the given data type. This assures that the initial value is valid for that data type, but it still may not be the "right" value for your server. Some data variable configuration extension methods (such as the ReadWriteValue Method) allow you (and force you to) specify the initial data (or just the value) directly as an argument in the method call.

Data Subscriptions

The OPC Wizard also retrieves the data from the ReadAttributeData Property of the data variable for the purpose of updating the data subscriptions. If your underlying system supports different rates of data retrieval, you may want to adjust the retrieval rate based on the sampling rate of the data subscriptions. The OPC Wizard maintains the current sampling interval, computed as the shortest of the sampling intervals of current data subscriptions, in the SamplingInterval Property of the data variable, and your code can take it from there. Also, when the value of this property changes, the SamplingIntervalChanged Event is raised, and your code can react to it accordingly.

The following example illustrates this approach.

.NET

// This example shows how to adjust to sampling interval changes in the push data provision model.
// You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-OPCStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using System.Threading;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.NodeSpace;
using Timer = System.Timers.Timer;

namespace UAServerDocExamples._UAServerNode
{
    class SamplingIntervalChanged
    {
        public static void Main1()
        {
            // Instantiate the server object.
            // By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            var server = new EasyUAServer();

            // Create a timer for pushing the data for OPC reads. In a real server the activity may also come from other
            // sources.
            var timer = new Timer { AutoReset = true };

            // Create a read-only data variable.
            var dataVariable = UADataVariable.CreateIn(server.Objects, "SubscribeToThisVariable")
                .ValueType<int>()
                .Writable(false);
            dataVariable.SamplingIntervalChanged += (sender, args) =>
            {
                // Obtain and display the new sampling interval.
                int samplingInterval = dataVariable.SamplingInterval;
                Console.WriteLine($"Sampling interval changed to: {samplingInterval}");

                // Adjust the timer interval accordingly. If the sampling interval is infinite, stop the timer. Otherwise,
                // set the timer interval to half of the sampling interval, but at least 1 millisecond, and start the timer.
                if (samplingInterval == Timeout.Infinite)
                    timer.Enabled = false;
                else
                {
                    timer.Interval = Math.Max(samplingInterval/2, 1);
                    timer.Enabled = true;
                }
                args.Handled = true;
            };
            
            // Set the read attribute data of the data variable to a random value whenever the timer interval elapses.
            var random = new Random();
            timer.Elapsed += (sender, args) => dataVariable.UpdateReadAttributeData(random.Next());

            // Start the server.
            Console.WriteLine("The server is starting...");
            server.Start();

            Console.WriteLine("The server is started.");
            Console.WriteLine();

            // Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...");
            Console.ReadLine();

            // Stop the server.
            Console.WriteLine("The server is stopping...");
            server.Stop();

            // Stop the timer.
            timer.Stop();

            Console.WriteLine("The server is stopped.");
        }
    }
}
' This example shows how to adjust to sampling interval changes in the push data provision model.
' You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports System
Imports System.Threading
Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.NodeSpace
Imports Timer = System.Timers.Timer

Namespace _UAServerNode
    Partial Friend Class SamplingIntervalChanged
        Shared Sub Main1()
            ' Instantiate the server object.
            ' By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            Dim server = New EasyUAServer()

            ' Create a timer for pushing the data for OPC reads. In a real server the activity may also come from other
            ' sources.
            Dim timer = New Timer With {.AutoReset = True}

            ' Create a read-only data variable.
            Dim dataVariable = UADataVariable.CreateIn(server.Objects, "SubscribeToThisVariable") _
                .ValueType(Of Integer)() _
                .Writable(False)

            AddHandler dataVariable.SamplingIntervalChanged, Sub(sender, args)
                                                                 ' Obtain and display the new sampling interval.
                                                                 Dim samplingInterval As Integer = dataVariable.SamplingInterval
                                                                 Console.WriteLine($"Sampling interval changed to: {samplingInterval}")

                                                                 ' Adjust the timer interval accordingly. If the sampling interval is infinite, stop the timer. Otherwise,
                                                                 ' set the timer interval to half of the sampling interval, but at least 1 millisecond, and start the timer.
                                                                 If samplingInterval = Timeout.Infinite Then
                                                                     timer.Enabled = False
                                                                 Else
                                                                     timer.Interval = Math.Max(samplingInterval / 2, 1)
                                                                     timer.Enabled = True
                                                                 End If
                                                                 args.Handled = True
                                                             End Sub

            ' Set the read attribute data of the data variable to a random value whenever the timer interval elapses.
            Dim random = New Random()
            AddHandler timer.Elapsed, Sub(sender, args) dataVariable.UpdateReadAttributeData(random.Next())

            ' Start the server.
            Console.WriteLine("The server is starting...")
            server.Start()

            Console.WriteLine("The server is started.")
            Console.WriteLine()

            ' Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...")
            Console.ReadLine()

            ' Stop the server.
            Console.WriteLine("The server is stopping...")
            server.Stop()

            Console.WriteLine("The server is stopped.")
        End Sub
    End Class
End Namespace

Starting and Stopping

The data collection that your code performs may need to be started and stopped together with the OPC server. To achieve this, you can tie it to the Starting Event and Stopped Event of the EasyUAServer object. The following example illustrates this approach.

.NET

// This example shows how to react to events in order to initiate and finalize data collection in the push data provision
// model.
// You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-OPCStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using System.Timers;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.NodeSpace;

namespace UAServerDocExamples._UAServerNode
{
    class Starting_Stopped
    {
        public static void Main1()
        {
            // Instantiate the server object.
            // By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            var server = new EasyUAServer();

            // Create a read-only data variable.
            var dataVariable = UADataVariable.CreateIn(server.Objects, "ReadThisVariable")
                .ValueType<int>()
                .Writable(false);

            dataVariable.Starting += (sender, args) =>
            {
                // Create a timer for pushing the data for OPC reads.
                var timer = new Timer
                {
                    Interval = 1000,
                    AutoReset = true,
                };

                // Set the read attribute data of the data variable to a random value whenever the timer interval elapses.
                // Note that this example shows the basic concept, however there is also an UpdateReadAttributeData method that
                // can be used in most cases to achieve slightly more concise code.
                var random = new Random();
                timer.Elapsed += (s, a) =>
                    dataVariable.ReadAttributeData = new UAAttributeData(random.Next(), DateTime.UtcNow);
                timer.Start();

                // Associate the timer with the data variable.
                dataVariable.State = timer;
            };
            dataVariable.Stopped += (sender, args) =>
            {
                // Obtain the timer associated with the data variable.
                var timer = (Timer)((UADataVariable)sender).State;

                // Stop the timer.
                timer.Stop();
            };
            
            // Start the server.
            Console.WriteLine("The server is starting...");
            server.Start();

            Console.WriteLine("The server is started.");
            Console.WriteLine();

            // Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...");
            Console.ReadLine();

            // Stop the server.
            Console.WriteLine("The server is stopping...");
            server.Stop();

            Console.WriteLine("The server is stopped.");
        }
    }
}
' This example shows how to react to events in order to initiate and finalize data collection in the push data provision
' model.
' You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports System
Imports System.Timers
Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.NodeSpace

Namespace _UAServerNode
    Partial Friend Class Starting_Stopped
        Shared Sub Main1()
            ' Instantiate the server object.
            ' By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            Dim server = New EasyUAServer()

            ' Create a read-only data variable.
            Dim dataVariable = UADataVariable.CreateIn(server.Objects, "ReadThisVariable") _
                .ValueType(Of Integer)() _
                .Writable(False)

            AddHandler dataVariable.Starting, Sub(sender, args)
                                                  ' Create a timer for pushing the data for OPC reads.
                                                  Dim timer = New Timer With
                                                  {
                                                    .Interval = 1000,
                                                    .AutoReset = True
                                                  }

                                                  ' Set the read attribute data of the data variable to a random value whenever the timer interval elapses.
                                                  ' Note that this example shows the basic concept, however there is also an UpdateReadAttributeData method that
                                                  ' can be used in most cases to achieve slightly more concise code.
                                                  Dim random = New Random()
                                                  AddHandler timer.Elapsed, Sub(s, a)
                                                                                dataVariable.ReadAttributeData = New UAAttributeData(random.Next(), DateTime.UtcNow)
                                                                            End Sub
                                                  timer.Start()

                                                  ' Associate the timer with the data variable.
                                                  dataVariable.State = timer
                                              End Sub

            AddHandler dataVariable.Stopped, Sub(sender, args)
                                                 ' Obtain the timer associated with the data variable.
                                                 Dim timer = CType(CType(sender, UADataVariable).State, Timer)

                                                 ' Stop the timer.
                                                 timer.Stop()
                                             End Sub

            ' Start the server.
            Console.WriteLine("The server is starting...")
            server.Start()

            Console.WriteLine("The server is started.")
            Console.WriteLine()

            ' Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...")
            Console.ReadLine()

            ' Stop the server.
            Console.WriteLine("The server is stopping...")
            server.Stop()

            Console.WriteLine("The server is stopped.")
        End Sub
    End Class
End Namespace
See Also

Reference

Examples - Server OPC Unified Architecture